home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 4 / QRZ Ham Radio Callsign Database - Volume 4.iso / files / tcpip / amiga / asrc29p.lha / bmutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-29  |  27.0 KB  |  1,030 lines

  1. /*
  2.  *   Simple mail user interface for KA9Q IP/TCP package.
  3.  *   A.D. Barksdale Garbee II, aka Bdale, N3EUA
  4.  *   Copyright 1986 Bdale Garbee, All Rights Reserved.
  5.  *   Permission granted for non-commercial copying and use, provided
  6.  *   this notice is retained.
  7.  *   Copyright 1987 1988 Dave Trulli NN2Z, All Rights Reserved.
  8.  *   Permission granted for non-commercial copying and use, provided
  9.  *   this notice is retained.
  10.  *
  11.  *   Ported to NOS at 900120 by Anders Klemets SM0RGV.
  12.  */
  13.  
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <ctype.h>
  17. #include <time.h>
  18. #include "global.h"
  19. #include "ftpserv.h"
  20. #include "smtp.h"
  21. #include "proc.h"
  22. #include "usock.h"
  23. #include "telnet.h"
  24. #include "timer.h"
  25. #include "files.h"
  26.  
  27. #define      SETVBUF
  28. #if   defined(UNIX) || defined(MICROSOFT)
  29. #include   <sys/types.h>
  30. #endif
  31.  
  32. #include <fcntl.h>
  33. #include "bm.h"
  34. #include "mailbox.h"
  35.  
  36. #ifdef SETVBUF
  37. #define      MYBUF   1024
  38. char   *stdinbuf = NULLCHAR;   /* the stdio buffer for the mail file */
  39. char   *stdoutbuf = NULLCHAR;   /* the stdio file io buffer for the temp file */
  40. #endif
  41.  
  42. extern long ftell();
  43. char Badmsg[] = "Invalid Message number %d\n";
  44. char Nomail[] = "No messages\n";
  45. static char Noaccess[] = "Unable to access %s\n";
  46. static int readnotes __ARGS((struct mbx *m,FILE *ifile,int update));
  47. static long isnewmail __ARGS((struct mbx *m));
  48. static int initnotes __ARGS((struct mbx *m));
  49. static int lockit __ARGS((struct mbx *m));
  50. static long fsize __ARGS((char *name));
  51. static void mfclose __ARGS((struct mbx *m));
  52.  
  53. static int initnotes(m)
  54. struct mbx *m;
  55. {
  56.    FILE *tmpfile();
  57.    FILE *ifile;
  58.    register struct let *cmsg;
  59.    char buf[256];
  60.    int i, ret;
  61.  
  62.    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  63.    if ((ifile = fopen(buf,READ_TEXT)) == NULLFILE)
  64.       return 0;
  65.    fseek(ifile,0L,2);    /* go to end of file */
  66.    m->mboxsize = ftell(ifile);
  67.    rewind(ifile);
  68.    if(!stricmp(m->area,m->name)) /* our private mail area */
  69.       m->mysize = m->mboxsize;
  70.    if ((m->mfile = tmpfile()) == NULLFILE) {
  71.       (void) fclose(ifile);
  72.       return -1;
  73.    }
  74. #ifdef   SETVBUF
  75.    if (stdinbuf == NULLCHAR)
  76.       stdinbuf = mallocw(MYBUF);
  77.    setvbuf(ifile, stdinbuf, _IOFBF, MYBUF);
  78.    if (stdoutbuf == NULLCHAR)
  79.       stdoutbuf = mallocw(MYBUF);
  80.    setvbuf(m->mfile, stdoutbuf, _IOFBF, MYBUF);
  81. #endif
  82.    m->nmsgs = 0;
  83.    m->current = 0;
  84.    m->change = 0;
  85.    m->newmsgs = 0;
  86.    m->anyread = 0;
  87.    /* Allocate space for reading messages */
  88.    free((char *)m->mbox);
  89.    m->mbox = (struct let *)callocw(Maxlet+1,sizeof(struct let));
  90.    ret = readnotes(m,ifile,0);
  91.    (void) fclose(ifile);
  92. #ifdef SETVBUF
  93.    free(stdinbuf);
  94.    stdinbuf = NULLCHAR;
  95. #endif
  96.    if (ret != 0)
  97.       return -1;
  98.    for (cmsg = &m->mbox[1],i = 1; i <= m->nmsgs; i++, cmsg++)  
  99.       if ((cmsg->status & BM_READ) == 0) {
  100.          m->newmsgs++;
  101.          if (m->current == 0)
  102.             m->current = i;
  103.       }
  104.    /* start at one if no new messages */
  105.    if (m->current == 0)
  106.       m->current++;
  107.    return 0;
  108. }
  109.  
  110. /* readnotes assumes that ifile is pointing to the first
  111.  * message that needs to be read.  For initial reads of a
  112.  * notesfile, this will be the beginning of the file.  For
  113.  * rereads when new mail arrives, it will be the first new
  114.  * message.  */
  115. static int readnotes(m,ifile,update)
  116. struct mbx *m;
  117. FILE *ifile ;
  118. int update;   /* true if this is not the initial read of the notesfile */
  119. {
  120.    char tstring[LINELEN];
  121.    long cpos;
  122.    register struct   let *cmsg;
  123.    register char *line;
  124.  
  125.    cmsg = (struct let *)NULL;
  126.    line = tstring;
  127.    while(fgets(line,LINELEN,ifile) != NULLCHAR) {
  128.       /* scan for begining of a message */
  129.       if(strncmp(line,"From ",5) == 0) {
  130.          pwait(NULL);
  131.          cpos = ftell(m->mfile);
  132.          fputs(line,m->mfile);
  133.          if (m->nmsgs == Maxlet) {
  134.             tprintf("Mail box full: > %d messages\n",Maxlet);
  135.             mfclose(m);
  136.             return -1;
  137.          }
  138.          m->nmsgs++;
  139.          cmsg = &m->mbox[m->nmsgs];
  140.          cmsg->start = cpos;
  141.          if(!update)
  142.             cmsg->status = 0;
  143.          cmsg->size = strlen(line);
  144.          while (fgets(line,LINELEN,ifile) != NULLCHAR) {
  145.             if (*line == '\n') { /* done header part */
  146.                cmsg->size++;
  147.                putc(*line, m->mfile);
  148.                break;
  149.             }
  150.             if (htype(line) == STATUS) {
  151.                if (line[8] == 'R') 
  152.                   cmsg->status |= BM_READ;
  153.                continue;
  154.             }
  155.             cmsg->size += strlen(line);
  156.             if (fputs(line,m->mfile) == EOF) {
  157.                tprintf("tmp file: %s",sys_errlist[errno]);
  158.                mfclose(m);
  159.                return -1;
  160.             }
  161.          }
  162.       } else if (cmsg) {
  163.          cmsg->size += strlen(line);
  164.          fputs(line,m->mfile);
  165.       }
  166.    }
  167.    return 0;
  168. }
  169.  
  170. /* list headers of a notesfile a message */
  171. int dolistnotes(argc,argv,p)
  172. int argc;
  173. char *argv[];
  174. void *p;
  175. {
  176.    struct mbx *m;
  177.    register struct let *cmsg;
  178.    register char *cp, *s;
  179.    char smtp_date[SLINELEN], smtp_from[SLINELEN], smtp_to[SLINELEN];
  180.    char smtp_subject[SLINELEN], tstring[LINELEN], type;
  181.    int start, stop;
  182.    long size;
  183.  
  184.    m = (struct mbx *) p;
  185.    if (m->mfile == NULLFILE) {
  186.       tprintf(Nomail);
  187.       return 0;
  188.    }
  189.  
  190.    if((m->stype == '>' || m->stype == '<') && argc == 1) {
  191.       tprintf("Search criterium needed !\n");
  192.       return 0;
  193.    }
  194.  
  195.    tprintf("Mail area: %s  %d message%s -  %d new\n\n"
  196.            "St.  #       TO     FROM    DATE  TIME   SIZE SUBJECT\n",
  197.            m->area, m->nmsgs, m->nmsgs == 1 ? " " : "s ", m->newmsgs);
  198.    stop = m->nmsgs;
  199.    if(m->stype == 'L') {      /* LL (List Latest) command */
  200.       if(argc > 1)
  201.          start = stop - atoi(argv[1]) + 1;
  202.       else
  203.          start = stop;
  204.    } else {
  205.       if((m->stype == '>' || m->stype == '<')) {
  206.          start = 1;
  207.          stop = m->nmsgs;
  208.       } else {
  209.          if(argc > 1)
  210.             start = atoi(argv[1]);
  211.          else
  212.             start = 1;
  213.          if(argc > 2)
  214.             stop = atoi(argv[2]);
  215.       }
  216.    }
  217.    if(stop > m->nmsgs)
  218.       stop = m->nmsgs;
  219.    if(start < 1 || start > stop) {
  220.       tprintf("Invalid range.\n");
  221.       return 0;
  222.    }
  223.    for (cmsg = &m->mbox[start]; start <= stop; start++, cmsg++) {
  224.       *smtp_date = '\0';
  225.       *smtp_from = '\0';
  226.       *smtp_subject = '\0';
  227.       *smtp_to = '\0';
  228.       type = ' ';
  229.       fseek(m->mfile,cmsg->start,0);
  230.       size = cmsg->size;
  231.       while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile)
  232.          != NULLCHAR) {
  233.          if (*tstring == '\n')   /* end of header */
  234.             break;
  235.          size -= strlen(tstring);
  236.          rip(tstring);
  237.          /* handle continuation later */
  238.          if (*tstring == ' '|| *tstring == '\t')
  239.             continue;
  240.          switch(htype(tstring)) {
  241.          case FROM:
  242.             cp = getaddress(tstring,0);
  243.             sprintf(smtp_from,"%s",
  244.                cp != NULLCHAR ? cp : "");
  245.             if((cp=strchr(smtp_from,'%')) != NULLCHAR)
  246.                *cp = '\0';
  247.             if((cp=strchr(smtp_from,'@')) != NULLCHAR)
  248.                *cp = '\0';
  249.             break;
  250.          case SUBJECT:
  251.             sprintf(smtp_subject,"%s",&tstring[9]);
  252.             break;
  253.          case DATE:
  254.             if ((cp = strchr(tstring,',')) == NULLCHAR)
  255.                cp = &tstring[6];
  256.             else
  257.                cp++;
  258.             /* skip spaces */
  259.             while (*cp == ' ') cp++;
  260.             if(strlen(cp) < 17)
  261.                break;    /* not a valid length */
  262.             s = smtp_date;
  263.             /* copy day */
  264.             if (atoi(cp) < 10 && *cp != '0') {
  265.                *s++ = ' ';
  266.             } else
  267.                *s++ = *cp++;
  268.             *s++ = *cp++;
  269.             *s++ = ' ';
  270.             *s = '\0';
  271.             while (*cp == ' ')
  272.                cp++;
  273.             strncat(s,cp,3);   /* copy month */
  274.             cp += 3;
  275.             while (*cp == ' ')
  276.                cp++;
  277.             /* skip year */
  278.             while (isdigit(*cp))
  279.                cp++;
  280.             /* copy time */
  281.             strncat(s,cp,6); /* space hour : min */
  282.             break;
  283.          case BBSTYPE:
  284.             type = tstring[16];
  285.             break;
  286.          case TO:
  287.             sprintf(smtp_to,"%s",&tstring[4]);
  288.             if((cp=strchr(smtp_to,'%')) != NULLCHAR)
  289.                *cp = '\0';
  290.             if((cp=strchr(smtp_to,'@')) != NULLCHAR)
  291.                *cp = '\0';
  292.             break;
  293.          case NOHEADER:
  294.             break;
  295.          }
  296.       }
  297.       if(m->stype == ' ' || m->stype == 'L' ||
  298.          (type == m->stype && m->stype != ' ') ||
  299.          ((m->stype == '<') && 
  300.           (strstr(strlwr(smtp_from),argv[1]) != NULLCHAR)) ||
  301.          ((m->stype == '>') && 
  302.           (strstr(strlwr(smtp_to),argv[1]) != NULLCHAR))) 
  303.         tprintf("%c%c%c%3d %8.8s %8.8s  %-13.13s %5ld %.35s\n",
  304.             (start == m->current ? '>' : ' '),
  305.             (cmsg->status & BM_DELETE ? 'D' : ' '),
  306.             (cmsg->status & BM_READ ? 'Y' : 'N'),
  307.             start, smtp_to, smtp_from, smtp_date,
  308.             cmsg->size, smtp_subject);
  309.    }
  310.    return 0;
  311. }
  312.  
  313. /*  save msg on stream - if noheader set don't output the header */
  314. int msgtofile(m,msg,tfile,noheader)
  315. struct mbx *m;
  316. int msg;
  317. FILE *tfile;   /* already open for write */
  318. int noheader;
  319. {
  320.    char tstring[LINELEN];
  321.    long size;
  322.  
  323.    if (m->mfile == NULLFILE) {
  324.       tprintf(Nomail);
  325.       return -1;
  326.    }
  327.    fseek(m->mfile,m->mbox[msg].start,0);
  328.    size = m->mbox[msg].size;
  329.  
  330.    if (noheader) {
  331.       /* skip header */
  332.       while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile)
  333.          != NULLCHAR) {
  334.          size -= strlen(tstring);
  335.          if (*tstring == '\n')
  336.             break;
  337.       }
  338.    }
  339.    while (size > 0 && fgets(tstring,sizeof(tstring),m->mfile)
  340.       != NULLCHAR) {
  341.       size -= strlen(tstring);
  342.       fputs(tstring,tfile);
  343.       if (ferror(tfile)) {
  344.          tprintf("Error writing mail file\n");
  345.          return -1;
  346.       }
  347.    }
  348.    return 0;
  349. }
  350.  
  351. /*  dodelmsg - delete message in current notesfile */
  352. int dodelmsg(argc,argv,p)
  353. int argc;
  354. char *argv[];
  355. void *p;
  356. {
  357.    struct mbx *m;
  358.    int msg,i;
  359.    m = (struct mbx *) p;
  360.    if (m->mfile == NULLFILE) {
  361.       tprintf(Nomail);
  362.       return 0;
  363.    }
  364.    for(i = 1; i < argc; ++i) {
  365.       msg = atoi(argv[i]);
  366.       if(msg < 0 || msg > m->nmsgs) {
  367.          tprintf(Badmsg,msg);
  368.          continue;
  369.       }
  370.       /* Check if we have permission to delete others mail */
  371.       if(!(m->privs & FTP_WRITE) && stricmp(m->area,m->name)) {
  372.          tprintf(Noperm);
  373.          return 0;
  374.       }
  375.       /* A second kill makes the message unkilled -- SM6RPZ */
  376.       if(m->mbox[msg].status & BM_DELETE) {
  377.               /* Message is killed - unkill it! */
  378.               m->mbox[msg].status &= ~BM_DELETE;
  379.               tprintf("Msg %d Unkilled.\n",msg);
  380.       } else {
  381.               /* Kill message */
  382.               m->mbox[msg].status |= BM_DELETE;
  383.               tprintf("Msg %d Killed.\n", msg);
  384.       }
  385.       m->change = 1;
  386.    }
  387.    return 0;
  388. }
  389.  
  390. /* close the temp file while coping mail back to the mailbox */
  391. int closenotes(m)
  392. struct mbx *m;
  393. {
  394.    register struct   let *cmsg;
  395.    register char *line;
  396.    char tstring[LINELEN], buf[256];
  397.    long size;
  398.    int i, nostatus, nodelete;
  399.    FILE *nfile;
  400.  
  401.    if (m->mfile == NULLFILE)
  402.       return 0;
  403.  
  404.    if(!m->change) {      /* no changes were made */
  405.       mfclose(m);
  406.       m->mboxsize = 0;
  407.       return 0;
  408.    }
  409.    /* If this area is a public message area, then we will not add a
  410.     * Status line to indicate that the message has been read.
  411.     */
  412.    nostatus = isarea(m->area);
  413.  
  414.    /* Don't delete messages from public message areas unless you are
  415.     * a BBS.
  416.     */
  417.    if(nostatus)
  418.       nodelete = !(m->privs & SYSOP_CMD);
  419.    else
  420.       nodelete = 0;
  421.  
  422.    /* See if any messages have been forwarded, otherwise just close
  423.     * the file and return since there is nothing to write back.
  424.     */
  425.    if(nostatus && nodelete) {
  426.       for(i=1; i <= m->nmsgs; ++i)
  427.          if(m->mbox[i].status & BM_FORWARDED)
  428.             break;
  429.       if(i > m->nmsgs) {
  430.          mfclose(m);
  431.          m->mboxsize = 0;
  432.          return 0;
  433.       }
  434.    }
  435.    line = tstring;
  436.    scanmail(m);
  437.    if(lockit(m))
  438.       return -1;
  439.    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  440.    if ((nfile = fopen(buf,WRITE_TEXT)) == NULLFILE) {
  441.       tprintf(Noaccess,buf);
  442.       mfclose(m);
  443.       m->mboxsize = 0;
  444.       rmlock(Mailspool,m->area);
  445.       return -1;
  446.    }
  447.    /* copy tmp file back to notes file */
  448.    for (cmsg = &m->mbox[1],i = 1; i <= m->nmsgs; i++, cmsg++) {
  449.       fseek(m->mfile,cmsg->start,0);
  450.       size = cmsg->size;
  451.       /* It is not possible to delete messages if nodelete is set */
  452.       if ((cmsg->status & BM_DELETE) && !nodelete)
  453.          continue;
  454.       /* copy the header */
  455.       while (size > 0 && fgets(line,LINELEN,m->mfile) != NULLCHAR) {
  456.          size -= strlen(line);
  457.          if (*line == '\n') {
  458.             if (cmsg->status & BM_FORWARDED)
  459.                fprintf(nfile,"%s%s\n",Hdrs[XFORWARD],
  460.                   m->name);
  461.             if ((cmsg->status & BM_READ) != 0 && !nostatus)
  462.                fprintf(nfile,"%sR\n",Hdrs[STATUS]);
  463.             fprintf(nfile,"\n");
  464.             break;
  465.          }
  466.          fputs(line,nfile);
  467.          /* pwait(NULL);  can cause problems if exiting NOS */
  468.       }
  469.       while (size > 0 && fgets(line,LINELEN,m->mfile) != NULLCHAR) {
  470.          fputs(line,nfile);
  471.          size -= strlen(line);
  472.          /* pwait(NULL);   dont want no damaged files */
  473.          if (ferror(nfile)) {
  474.             tprintf("Error writing mail file\n");
  475.             (void) fclose(nfile);
  476.             mfclose(m);
  477.             m->mboxsize = 0;
  478.             rmlock(Mailspool,m->area);
  479.             return -1;
  480.          }
  481.       }
  482.    }
  483.    m->nmsgs = 0;
  484.    if (!stricmp(m->name,m->area))
  485.       m->mysize = ftell(nfile); /* Update the size of our mailbox */
  486.    /* remove a zero length file */
  487.    if (ftell(nfile) == 0L)
  488.       (void) unlink(buf);
  489.    (void) fclose(nfile);
  490.    mfclose(m);
  491.    m->mboxsize = 0;
  492.    rmlock(Mailspool,m->area);
  493.    pwait(NULL);
  494.    return 0;
  495. }
  496.  
  497. /* Returns 1 if name is a public message Area, 0 otherwise */
  498. int isarea(name)
  499. char *name;
  500. {
  501.    char buf[LINELEN], *cp;
  502.    FILE *fp;
  503.    if((fp = fopen(Arealist,READ_TEXT)) == NULLFILE)
  504.       return 0;
  505.    while(fgets(buf,sizeof(buf),fp) != NULLCHAR) {
  506.       /* The first word on each line is all that matters */
  507.       if((cp = strchr(buf,' ')) == NULLCHAR)
  508.          if((cp = strchr(buf,'\t')) == NULLCHAR)
  509.             continue;
  510.       *cp = '\0';
  511.       if((cp = strchr(buf,'\t')) != NULLCHAR)
  512.          *cp = '\0';
  513.       if(stricmp(name,buf) == 0) {   /* found it */
  514.          fclose(fp);
  515.          return 1;
  516.       }
  517.    }
  518.    fclose(fp);
  519.    return 0;
  520. }
  521.  
  522. static int lockit(m)
  523. struct mbx *m;
  524. {
  525.    int c, cnt = 0;
  526.  
  527.    while(mlock(Mailspool,m->area)) {
  528.       pause(1000/MSPTICK);   /* Wait one second */
  529.       if(++cnt == 10) {
  530.          cnt = 0;
  531.          c = keywait("Mail file is busy, Abort or Retry ? ",0);
  532.          if (c == 'A' || c == 'a' || c == EOF) {
  533.             mfclose(m);
  534.             return 1;
  535.          }
  536.       }
  537.    }
  538.    return 0;
  539. }
  540.  
  541. /* read the next message or the current one if new */
  542. int doreadnext(argc,argv,p)
  543. int argc;
  544. char *argv[];
  545. void *p;
  546. {
  547.    struct mbx *m;
  548.    char buf[10], *newargv[2];
  549.    m = (struct mbx *) p;
  550.    if (m->mfile == NULLFILE)
  551.       return 0;
  552.    if ((m->mbox[m->current].status & BM_READ) != 0) {
  553.       if (m->current == 1 && m->anyread == 0)
  554.          ;
  555.       else if (m->current < m->nmsgs) {
  556.          m->current++;
  557.       } else {
  558.          tprintf("Last message\n");
  559.          return 0;
  560.       }
  561.    }
  562.    sprintf(buf,"%d",m->current);
  563.    newargv[0] = "read";
  564.    newargv[1] = buf;
  565.    m->anyread = 1;
  566.    return doreadmsg(2,newargv,p);
  567. }
  568.  
  569. /*  display message on the crt given msg number */
  570. int doreadmsg(argc,argv,p)
  571. int argc;
  572. char *argv[];
  573. void *p;
  574. {
  575.    struct mbx *m;
  576.    register int c, col, lin;
  577.    char buf[MAXCOL+2], *cp, *cp2;
  578.    int msg, cnt, i, tcpcon, verbose = 0, mbxheader = 0, pathcol;
  579.    int header, lastheader;
  580.    long size;
  581.    struct usock *up;
  582.  
  583.    m = (struct mbx *) p;
  584.    if (m->mfile == NULLFILE) {
  585.       tprintf(Nomail);
  586.       return 0;
  587.    }
  588.    if(*argv[0] == 'v')
  589.       verbose = 1;      /* display all header lines */
  590.    if((up = itop(Curproc->output)) != NULLUSOCK && up->type == TYPE_TCP)
  591.       tcpcon = 1;   /* This is a TCP (i.e. Telnet) connection */
  592.    else
  593.       tcpcon = 0;
  594.    lin = MAXLIN-1;
  595.    for(i = 1; i < argc; ++i) {
  596.       msg = atoi(argv[i]);
  597.       if( msg < 1 || msg > m->nmsgs) {
  598.          tprintf(Badmsg,msg);
  599.          return 0;
  600.       }
  601.       fseek(m->mfile,m->mbox[msg].start,0);
  602.       size = m->mbox[msg].size;
  603.       m->current = msg;
  604.       header = NOHEADER;
  605.  
  606.       tprintf("Message #%d %s\n", msg,
  607.          m->mbox[msg].status & BM_DELETE ? "[Deleted]" : "");
  608.       if ((m->mbox[msg].status & BM_READ) == 0) {
  609.          m->mbox[msg].status |= BM_READ;
  610.          m->change = 1;
  611.          m->newmsgs--;
  612.       }
  613.       --lin;
  614.       while (!feof(m->mfile) && size > 0) {
  615.          for (col = 0;  col < MAXCOL;) {
  616.             c = getc(m->mfile);
  617.             size--;
  618.             if (feof(m->mfile) || size == 0) /* end this line */
  619.                break;
  620.             if (c == '\t') {
  621.                cnt = col + 8 - (col & 7);
  622.                if (cnt >= MAXCOL) /* end this line */
  623.                   break;
  624.                while (col < cnt)
  625.                   buf[col++] = ' ';
  626.             } else {
  627.                if (c == '\n')
  628.                   break;
  629.                buf[col++] = c;
  630.             }
  631.          }
  632.          if(col < MAXCOL)
  633.             buf[col++] = '\n';
  634.          buf[col] = '\0';
  635.          if(mbxheader > 0) {
  636.             /* Digest R: lines and display as a Path: line */
  637.             if(strncmp(buf,"R:",2) != 0 ||
  638.                (cp = strchr(buf,'@')) == NULLCHAR) {
  639.                tputc('\n');
  640.                mbxheader = -1; /* don't get here again */
  641.                verbose = 1;
  642.             } else {
  643.                if(*(++cp) == ':')
  644.                   ++cp;
  645.                for(cp2 = cp; isalnum(*cp2); ++cp2)  ;
  646.                *cp2 = '\0';
  647.                if(mbxheader++ == 1) {
  648.                   tputs("Path: ");
  649.                   pathcol = 5;
  650.                   --lin;
  651.                } else {
  652.                   tputc('!');
  653.                   if(++pathcol + strlen(cp) > MAXCOL-2) {
  654.                      tputs("\n      ");
  655.                      pathcol = 5;
  656.                      --lin;
  657.                   }
  658.                }
  659.                tputs(cp);
  660.                pathcol += strlen(cp);
  661.             }
  662.          }
  663.          if(col == 1 && !verbose && !mbxheader)
  664.             /* last header line reached */
  665.             mbxheader = 1;
  666.          if(verbose)
  667.             tputs(buf);
  668.          else {
  669.             lastheader = header;
  670.             if(!isspace(*buf))
  671.                header = htype(buf);
  672.             else
  673.                header = lastheader;
  674.             switch(header) {
  675.             case TO:
  676.             case CC:
  677.             case FROM:
  678.             case DATE:
  679.             case SUBJECT:
  680.                tputs(buf);
  681.                break;
  682.             default:
  683.                ++lin;
  684.             }
  685.          }
  686.          if(tcpcon && --lin == 0){
  687.             if(m->flowpage != 0) {
  688.                tprintf("--More--%c%c%c",IAC,WILL,TN_ECHO);
  689.                usflush(Curproc->output);
  690.                /* discard the response */
  691.                while((c = recvchar(Curproc->input)) == IAC){
  692.                   c = recvchar(Curproc->input);
  693.                   if(c > 250 && c < 255)
  694.                      recvchar(Curproc->input);
  695.                }
  696.                tprintf("\r           \r%c%c%c",IAC,WONT,TN_ECHO);
  697.                usflush(Curproc->output);
  698.             }
  699.             lin = MAXLIN-1;
  700.             if(c == -1 || c == 'q' || c == 'Q')
  701.                break;
  702.             if(c == '\n' || c == '\r')
  703.                lin = 1;
  704.          }
  705.       }
  706.    }
  707.    return 0;
  708. }
  709.  
  710. /* Set up m->to when replying to a message.
  711.    The subject is returned in m->line.  */
  712. int mbx_reply(argc,argv,m,cclist,rhdr)
  713. int argc;
  714. char *argv[];
  715. struct mbx *m;
  716. struct list **cclist;   /* Pointer to buffer for pointers to cc recipients */
  717. char **rhdr;      /* Pointer to buffer for extra reply headers */
  718. {
  719.    char subject[MBXLINE], *msgid = NULLCHAR, *date = NULLCHAR;
  720.    char *cp, *cp2, *nic, newto[80];
  721.    int msg, lastheader, header = NOHEADER, FoundRColon;
  722.    long size;
  723.  
  724.    /* Free anything that might be allocated
  725.     * since the last call to mbx_to() or mbx_reply()
  726.     */
  727.    free(m->to);
  728.    m->to = NULLCHAR;
  729.    free(m->tofrom);
  730.    m->tofrom = NULLCHAR;
  731.    free(m->tomsgid);
  732.    m->tomsgid = NULLCHAR;
  733.    free(m->origto);
  734.    m->origto = NULLCHAR;
  735.    subject[0] = '\0';
  736.  
  737.    if(argc == 1)
  738.       msg = m->current;
  739.    else
  740.       msg = atoi(argv[1]);
  741.    if (m->mfile == NULLFILE) {
  742.       if(m->sid & MBX_SID)
  743.          tputs("NO - ");
  744.       tputs(Nomail);
  745.       return 0;
  746.    }
  747.    if(msg < 1 || msg > m->nmsgs) {
  748.       if(m->sid & MBX_SID)
  749.          tputs("NO - ");
  750.       tputs(Badmsg);
  751.       return -1;
  752.    }
  753.    fseek(m->mfile,m->mbox[msg].start,0);
  754.    size = m->mbox[msg].size;
  755.    m->current = msg;
  756.    FoundRColon = 0;
  757.    while(size > 0 && fgets(m->line,MBXLINE-1,m->mfile) != NULLCHAR) {
  758.       size -= strlen(m->line);
  759. #ifdef xxx
  760.       if(m->line[0] == '\n')   /* end of rfc822 header */
  761.          break;
  762. #endif
  763.       rip(m->line);
  764.       lastheader = header;
  765.       if(!isspace(m->line[0])) {
  766.          header = htype(m->line);
  767.          lastheader = NOHEADER;
  768.       }
  769.       switch(header) {
  770.       case SUBJECT:
  771.          if(strlen(m->line) > 11 && !strnicmp(&m->line[9],"Re:",3))
  772.             strcpy(subject,&m->line[9]);
  773.          else
  774.             sprintf(subject,"Re: %s",&m->line[9]);
  775.          break;
  776.       case FROM:
  777.          if(m->to == NULLCHAR && (cp = getaddress(m->line,0)) !=
  778.             NULLCHAR)
  779.             m->to = strdup(cp);
  780.          break;
  781.       case RCOLON:
  782.          FoundRColon = 1;
  783.          nic = strstr(strdup(m->line),"@");
  784.          if((cp2=strchr(nic,' ')) != NULLCHAR)
  785.             *cp2 = '\0';
  786.          break;
  787.       case REPLYTO:
  788.          if((cp = getaddress(m->line,0)) != NULLCHAR) {
  789.             free(m->to);
  790.             m->to = strdup(cp);
  791.          }
  792.          break;
  793.       case MSGID:
  794.          free(msgid);
  795.          msgid = strdup(&m->line[12]);
  796.          break;
  797.       case DATE:
  798.          free(date);
  799.          date = strdup(&m->line[6]);
  800.          break;
  801.       case TO:
  802.       case CC:
  803.          /* Put addresses on To and Cc lines in cclist */
  804.          cp = m->line;
  805.          m->line[strlen(cp)+1] = '\0';   /* add extra null at end */
  806.          for(;;) {
  807.             if((cp = getaddress(cp,lastheader == header ||
  808.                cp != m->line)) == NULLCHAR)
  809.                break;
  810.             addlist(cclist,cp,0);
  811.             /* skip to next address, if any */
  812.             cp += strlen(cp) + 1;
  813.          }
  814.          break;
  815.       }
  816.    }
  817.  
  818.    if(FoundRColon) { 
  819.       if((cp2=strchr(m->to,'%')) != NULLCHAR)
  820.             *cp2 = '\0';
  821.       if((cp2=strchr(m->to,'@')) != NULLCHAR)
  822.             *cp2 = '\0';
  823.  
  824.       if((cp2=strchr(nic,':')) != NULLCHAR)
  825.             cp2 = &nic[2];
  826.       else
  827.             cp2 = &nic[1];
  828.  
  829.       sprintf(newto, "%s@%s", m->to, cp2);
  830.  
  831.       m->to = strdup(newto);
  832.    } 
  833.  
  834.    if(msgid != NULLCHAR || date != NULLCHAR) {
  835.       *rhdr = mallocw(LINELEN);
  836.       sprintf(*rhdr,"In-Reply-To: your message ");
  837.       if(date != NULLCHAR) {
  838.          sprintf(m->line,"of %s.\n",date);
  839.          strcat(*rhdr,m->line);
  840.          if(msgid != NULLCHAR)
  841.             strcat(*rhdr,"             ");
  842.       }
  843.       if(msgid != NULLCHAR) {
  844.          sprintf(m->line,"%s\n",msgid);
  845.          strcat(*rhdr,m->line);
  846.       }
  847.       free(msgid);
  848.       free(date);
  849.    }
  850.    strcpy(m->line,subject);
  851.    return 0;
  852. }
  853.  
  854. void scanmail(m)       /* Get any new mail */
  855. struct mbx *m;
  856. {
  857.    FILE *nfile;
  858.    int ret, cnt;
  859.    char buf[256];
  860.    long diff;
  861.  
  862.    if ((diff = isnewmail(m)) == 0L)
  863.       return;
  864.    if(lockit(m))
  865.       return;
  866.    if(m->mfile == NULLFILE || diff < 0L) {
  867.       /* This is the first time scanmail is called, or the
  868.        * mail file size has decreased. In the latter case,
  869.        * any changes we did to this area will be lost, but this
  870.        * is not fatal.
  871.        */
  872.       initnotes(m);
  873.       rmlock(Mailspool,m->area);
  874.       return;
  875.    }
  876.    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  877.    if ((nfile = fopen(buf,READ_TEXT)) == NULLFILE)
  878.       tprintf(Noaccess,buf);
  879.    else {
  880.       /* rewind tempfile */
  881.       fseek(m->mfile,0L,0);
  882.       cnt = m->nmsgs;
  883.       /* Reread all messages since size they may have changed
  884.        * in size after a X-Forwarded-To line was added. */
  885.       m->nmsgs = 0;
  886.       ret = readnotes(m,nfile,1);   /* get the mail */
  887.       m->newmsgs += m->nmsgs - cnt;
  888.       m->mboxsize = ftell(nfile);
  889.       if(!stricmp(m->name,m->area))
  890.          m->mysize = m->mboxsize;
  891.       (void) fclose(nfile);
  892.       if (ret != 0)
  893.          tprintf("Error updating mail file\n");
  894.    }
  895.    rmlock(Mailspool,m->area);
  896. }
  897.  
  898. /* Check the current mailbox to see if new mail has arrived.
  899.  * Returns the difference in size.  */
  900. static long isnewmail(m)
  901. struct mbx *m;
  902. {
  903.    char buf[256];
  904.    sprintf(buf,"%s/%s.txt",Mailspool,m->area);
  905.    return fsize(buf) - m->mboxsize;
  906. }
  907.  
  908. /* Check if the private mail area has changed */
  909. long isnewprivmail(m)
  910. struct mbx *m;
  911. {
  912.    long cnt;
  913.    char buf[256];
  914.    sprintf(buf,"%s/%s.txt",Mailspool,m->name);
  915.    cnt = m->mysize;
  916.    m->mysize = fsize(buf);
  917.    return m->mysize - cnt; /* != 0 not more than once */
  918. }
  919.  
  920. char *Hdrs[] = {
  921.    "Approved: ",
  922.    "From: ",
  923.    "To: ",
  924.    "Date: ",
  925.    "Message-Id: ",
  926.    "Subject: ",
  927.    "Received: ",
  928.    "Sender: ",
  929.    "Reply-To: ",
  930.    "Status: ",
  931.    "X-BBS-Msg-Type: ",
  932.    "X-Forwarded-To: ",
  933.    "Cc: ",
  934.    "Comment: ",
  935.    "Organisation: ",
  936.    "Return-Receipt-To: ",
  937.    "Apparently-To: ",
  938.    "Newsgroups: ",
  939.    "R:",
  940.    NULLCHAR
  941. };
  942.  
  943. /* return the header type */
  944. int htype(s)
  945. char *s;
  946. {
  947.    register char *p;
  948.    register int i;
  949.  
  950.    p = s;
  951.    /* check to see if there is a ':' before and white space */
  952.    while (*p != '\0' && *p != ' ' && *p != ':')
  953.       p++;
  954.    if (*p != ':')
  955.       return NOHEADER;
  956.  
  957.    for (i = 0; Hdrs[i] != NULLCHAR; i++) {
  958.       if (strnicmp(Hdrs[i],s,strlen(Hdrs[i])) == 0)
  959.          return i;
  960.    }
  961.    return UNKNOWN;
  962. }
  963.  
  964. /* This function returns the length of a file. The proper thing would be
  965.  * to use stat(), but it fails when using DesqView together with Turbo-C
  966.  * code.  */
  967. static long fsize(name)
  968. char *name;
  969. {
  970.    long cnt;
  971.    FILE *fp;
  972.    if((fp = fopen(name,READ_TEXT)) == NULLFILE)
  973.       return -1L;
  974.    fseek(fp,0L,2);
  975.    cnt = ftell(fp);
  976.    fclose(fp);
  977.    return cnt;
  978. }
  979.  
  980. /* close the temporary mail file */
  981. static void mfclose(m)
  982. struct mbx *m;
  983. {
  984.    if(m->mfile != NULLFILE)
  985.       fclose(m->mfile);
  986.    m->mfile = NULLFILE;
  987. #ifdef SETVBUF
  988.    free(stdoutbuf);
  989.    stdoutbuf = NULLCHAR;
  990. #endif
  991. }
  992.  
  993. /* Parse a string in the "Text: <user@host>" or "Text: user@host (Text)"
  994.  * format for the address user@host.
  995.  */
  996. char *getaddress(string,cont)
  997. char *string;
  998. int cont;      /* true if string is a continued header line */
  999. {
  1000.    char *cp, *ap = NULLCHAR;
  1001.    int par = 0;
  1002.    if((cp = getname(string)) != NULLCHAR) /* Look for <> style address */
  1003.       return cp;
  1004.    cp = string;
  1005.    if(!cont)
  1006.       if((cp = strchr(string,':')) == NULLCHAR)   /* Skip the token */
  1007.          return NULLCHAR;
  1008.       else
  1009.          ++cp;
  1010.    for(; *cp != '\0'; ++cp) {
  1011.       if(par && *cp == ')') {
  1012.          --par;
  1013.          continue;
  1014.       }
  1015.       if(*cp == '(')      /* Ignore text within parenthesis */
  1016.          ++par;
  1017.       if(par)
  1018.          continue;
  1019.       if(*cp == ' ' || *cp == '\t' || *cp == ',') {
  1020.          if(ap != NULLCHAR)
  1021.             break;
  1022.          continue;
  1023.       }
  1024.       if(ap == NULLCHAR)
  1025.          ap = cp;
  1026.    }
  1027.    *cp = '\0';
  1028.    return ap;
  1029. }
  1030.